#include "Subsystems/SubsystemInventories.h"

#include "Managers/GameManager.h"
#include "Managers/ChangeManager.h"
#include "Components/ComponentInventory.h"
#include "Components/ComponentCreativeInventory.h"
#include "Components/ComponentFurnitureInventory.h"
#include "Components/ComponentClothing.h"
#include "Components/ComponentCraftingTable.h"
#include "Components/ComponentChest.h"
#include "Components/ComponentFurnace.h"

#include "Terrain/Terrain.h"
#include "Network/PackageManager.h"
#include "Game/TableComponent.hpp"

#include "Log.h"
#include "GameSingleton.h"
#include "GameRegistry.hpp"


namespace PocketSurvival
{
    /***********************************************************************************************************
     * @brief 给库存对象分配ID，并保存进库存表里面
     * @param inventory 库存对象指针，不能为nullptr
     * @return 库存ID
     ***********************************************************************************************************/
    uint32_t SubsystemInventories::registerInventory(IInventory *inventory)
    {
        std::lock_guard<std::mutex> lk(m_mutex);
        if (m_idStack.getStackSize() > 0)
        {
            uint32_t id = m_idStack.top();
            m_idStack.pop();
            if (m_inventoryMap.find(id) != m_inventoryMap.end() && m_inventoryMap[id] != nullptr)
            {
                throw std::runtime_error(fmt::format("inventory表存在冲突的id: {}", id));
            }
            inventory->id = id;
            m_inventoryMap[id] = inventory;
            return id;
        }
        while (m_endID < UINT32_MAX)
        {
            if (m_inventoryMap.find(m_endID) == m_inventoryMap.end() || m_inventoryMap[m_endID] == nullptr)
            {
                inventory->id = m_endID;
                m_inventoryMap[m_endID] = inventory;
                m_endID++;
                return inventory->id;
            }
            m_endID++;
        }
        throw std::runtime_error(fmt::format("无法给库存分配ID，已经达到最大值: {}", m_endID));
    }

    /***********************************************************************************************************
     * @brief 通过id查找库存对象
     * @param id 库存对象的id
     * @return 库存对象，查找不到时为nullptr
     ***********************************************************************************************************/
    IInventory *SubsystemInventories::getInventory(uint32_t id)
    {
        std::lock_guard<std::mutex> lk(m_mutex);
        auto it = m_inventoryMap.find(id);
        if (it == m_inventoryMap.end())
        {
            return nullptr;
        }
        return it->second;
    }

    /***********************************************************************************************************
     * @brief 通过id移除库存表对象记录
     * @param id 库存对象的id
     * @return true:成功 | false:失败
     ***********************************************************************************************************/
    bool SubsystemInventories::removeInventoryById(uint32_t id)
    {
        // Log::Info(fmt::format("回收 Inventory ID : {}", id));
        std::lock_guard<std::mutex> lk(m_mutex);
        auto it = m_inventoryMap.find(id);
        if (it != m_inventoryMap.end() && it->second != nullptr)
        {
            it->second = nullptr;
            m_idStack.push(id);
            return true;
        }
        return false;
    }
    
    /***********************************************************************************************************
     * @brief 处理背包内物品的移动
     * @param sourceId 移出物品的背包ID
     * @param sourceSlotIndex 移出物品的背包的槽位
     * @param targetId 接收物品的背包ID
     * @param targetSlotIndex 接收物品的背包的槽位
     * @param count 移动物品的数量
     * @return true:成功 | false:失败
     ***********************************************************************************************************/
    bool SubsystemInventories::handleMoveItem(uint32_t sourceId, uint32_t sourceSlotIndex, uint32_t targetId, uint32_t targetSlotIndex, uint32_t count)
    {
        IInventory *sourceInventory = getInventory(sourceId);
        IInventory *targetInventory = getInventory(targetId);
        if(sourceInventory != nullptr && targetInventory != nullptr)
        {
            int32_t sourceValue = sourceInventory->getSlotValue(sourceSlotIndex);
            uint32_t sourceCount = sourceInventory->getSlotCount(sourceSlotIndex);
            int32_t targetValue = targetInventory->getSlotValue(targetSlotIndex);
            uint32_t targetCount = targetInventory->getSlotCount(targetSlotIndex);
            
            if(targetCount == 0 || sourceValue == targetValue)
            {
                uint32_t num = MathUtils::Min<uint32_t>(targetInventory->getSlotCapacity(targetSlotIndex, sourceValue) - targetCount, sourceCount, count);
                if(num > 0)
                {
                    uint32_t count2 = sourceInventory->removeSlotItems(sourceSlotIndex, num);
					targetInventory->addSlotItems(targetSlotIndex, sourceValue, count2);
                    return true;
                }
            }
        }
        return false;
    }

    /***********************************************************************************************************
     * @brief 处理背包内物品的拖拽
     * @param sourceId 移出物品的背包ID
     * @param sourceSlotIndex 移出物品的背包的槽位
     * @param dragMode 拖拽方式
     * @param targetId 接收物品的背包ID
     * @param targetSlotIndex 接收物品的背包的槽位
     * @param processingOnly 仅处理
     * @return true:成功 | false:失败
     ***********************************************************************************************************/
    bool SubsystemInventories::handleDragDrop(uint32_t sourceId, uint32_t sourceSlotIndex, DragMode dragMode, uint32_t targetId, uint32_t targetSlotIndex, bool processingOnly)
    {
        bool result = false;
        IInventory *sourceInventory = getInventory(sourceId);
        IInventory *targetInventory = getInventory(targetId);
        if(sourceInventory != nullptr && targetInventory != nullptr)
        {
            uint32_t sourceValue = sourceInventory->getSlotValue(sourceSlotIndex);
            uint32_t sourceCount = sourceInventory->getSlotCount(sourceSlotIndex);
            uint32_t targetValue = targetInventory->getSlotValue(targetSlotIndex);
            uint32_t targetCount = targetInventory->getSlotCount(targetSlotIndex);
			uint32_t slotCapacity = targetInventory->getSlotCapacity(targetSlotIndex, sourceValue);
			uint32_t slotProcessCapacity = targetInventory->getSlotProcessCapacity(targetSlotIndex, sourceValue);

            if (dragMode == DragMode::SingleItem)
			{
				sourceCount = MathUtils::Min<uint32_t>(sourceCount, 1);
			}
            if(slotProcessCapacity > 0)
            {
                uint32_t num6 = MathUtils::Min<uint32_t>(sourceCount, slotProcessCapacity);
                uint32_t processCount = sourceInventory->removeSlotItems(sourceSlotIndex, num6);
                int32_t processedValue;
                uint32_t processedCount;
				targetInventory->processSlotItems(sourceInventory, sourceSlotIndex, targetSlotIndex, sourceValue, sourceCount, processCount, processedValue, processedCount);
				if (processedValue != 0 && processedCount != 0)
				{
					uint32_t count = MathUtils::Min<uint32_t>(sourceInventory->getSlotCapacity(sourceSlotIndex, processedValue), processedCount);
					sourceInventory->addSlotItems(sourceSlotIndex, processedValue, count);
				}
                result = true;
            }
            else if(processingOnly == false && (targetCount == 0 || sourceValue == targetValue) && targetCount < slotCapacity)
            {
                uint32_t num2 = MathUtils::Min<uint32_t>(slotCapacity - targetCount, sourceCount);
				if (num2 > 0)
				{
					uint32_t count2 = sourceInventory->removeSlotItems(sourceSlotIndex, num2);
					targetInventory->addSlotItems(targetSlotIndex, sourceValue, count2);
                    result = true;
				}
            }
            else if(processingOnly == false && 
                targetInventory->getSlotCapacity(targetSlotIndex, sourceValue) >= sourceCount && 
                sourceInventory->getSlotCapacity(sourceSlotIndex, targetValue) >= targetCount &&
                sourceInventory->getSlotCount(sourceSlotIndex) == sourceCount)
            {
				int count3 = targetInventory->removeSlotItems(targetSlotIndex, targetCount);
				int count4 = sourceInventory->removeSlotItems(sourceSlotIndex, sourceCount);
				targetInventory->addSlotItems(targetSlotIndex, sourceValue, count4);
				sourceInventory->addSlotItems(sourceSlotIndex, targetValue, count3);
                result = true;
            }
        }
        return result;
    }

    /***********************************************************************************************************
     * @brief 同步客户端背包的槽位
     * @param inventory 哪个背包
     * @param slotIndex 背包的哪个位置
     ***********************************************************************************************************/
    void SubsystemInventories::pushSyncItem(IInventory *inventory, int32_t slotIndex)
    {
        GameSingleton::changeManager->inventoryChange(inventory, slotIndex);
    }


    // 添加实体的话，要遍历实体内的仓库组件，并赋值仓库id
    void SubsystemInventories::onEntityAdded(entt::entity entity)
    {
        TableComponent *tableCom = GameSingleton::gameRegistry->get<TableComponent*>(entity);
        std::vector<IInventory *> inventoryVec;
        tableCom->dynamicFindComponent<IInventory>(inventoryVec);
        for (IInventory *inventory : inventoryVec)
        {
            uint32_t id = registerInventory(inventory);
            // Log::Info(fmt::format("注册 Inventory ID : {}", id));
        }
    }

    // 移除实体的话，要回收对应的仓库id
    void SubsystemInventories::onEntityRemoved(entt::entity entity)
    {
        TableComponent *tableCom = GameSingleton::gameRegistry->get<TableComponent*>(entity);
        
        std::vector<IInventory *> inventoryVec;
        tableCom->dynamicFindComponent<IInventory>(inventoryVec);
        for (IInventory *inventory : inventoryVec)
        {
            removeInventory(inventory);
        }
    }

    void SubsystemInventories::load(const nlohmann::json &jsonData) {}
    void SubsystemInventories::save(nlohmann::json &jsonData) {}
    void SubsystemInventories::saveToMsgJson(nlohmann::json &jsonData) {}

    static const std::string subsystemName = "Inventories";
    const std::string &SubsystemInventories::getName() const
    {
        return subsystemName;
    }
} // namespace PocketSurvival
